#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

int buffer1[4], buffer2[4];
int buffer1_in = 0, buffer1_out = 0, buffer2_in = 0, buffer2_out = 0;

struct sema_t {
    int value;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
};

void put_item(int item, int x)
{
    if (x == 1)
    {
        buffer1[buffer1_in] = item;
        buffer1_in = (buffer1_in + 1) % 4;
    }
    else
    {
        buffer2[buffer2_in] = item;
        buffer2_in = (buffer2_in + 1) % 4;
    }
}

int get_item(int x)
{
    int item;
    if (x == 1)
    {
        item = buffer1[buffer1_out];
        buffer1_out = (buffer1_out + 1) % 4;
    }
    else
    {
        item = buffer2[buffer2_out];
        buffer2_out = (buffer2_out + 1) % 4;
    }
    return item;
}

void sema_init(struct sema_t *sema,int value)
{
    sema->value=value;
    pthread_mutex_init(&sema->mutex,NULL);
    pthread_cond_init(&sema->cond,NULL);
}

void sema_wait(struct sema_t *sema)
{
    pthread_mutex_lock(&sema->mutex);
    while(sema->value<=0)
        pthread_cond_wait(&sema->cond,&sema->mutex);
    sema->value--;
    pthread_mutex_unlock(&sema->mutex);
}

void sema_signal(struct sema_t *sema)
{
    pthread_mutex_lock(&sema->mutex);
    sema->value++;
    pthread_cond_signal(&sema->cond);
    pthread_mutex_unlock(&sema->mutex);
}

struct sema_t mutex1,mutex2,empty_buffer1,full_buffer1,empty_buffer2,full_buffer2;

void *producer()
{
    int item;
    for(int i=0;i<8;i++)
    {
        sema_wait(&empty_buffer1);
        sema_wait(&mutex1);
        item = 'a'+i;
        put_item(item,1);
        sema_signal(&mutex1);
        sema_signal(&full_buffer1);
    }
}

void *consumer()
{
    int item;
    for(int i=0;i<8;i++)
    {
        sema_wait(&full_buffer2);
        sema_wait(&mutex2);
        item=get_item(2);
        printf("%c\n",item);
        sema_signal(&mutex2);
        sema_signal(&empty_buffer2);
    }
}

void *counter()
{
    int item;
    for(int i=0;i<8;i++)
    {
        sema_wait(&full_buffer1);
        sema_wait(&mutex1);
        item = get_item(1);
        sema_signal(&mutex1);
        sema_signal(&empty_buffer1);

        sema_wait(&empty_buffer2);
        sema_wait(&mutex2);
        item-=32;
        put_item(item,2);
        sema_signal(&mutex2);
        sema_signal(&full_buffer2);
    }
}

int main()
{
    pthread_t producer_tid,counter_tid,consumer_tid;
    sema_init(&mutex1,1);
    sema_init(&mutex2,1);
    sema_init(&empty_buffer1,3);
    sema_init(&empty_buffer2,3);
    sema_init(&full_buffer1,0);
    sema_init(&full_buffer2,0);
    pthread_create(&producer_tid,NULL,producer,NULL);
    pthread_create(&counter_tid,NULL,counter,NULL);
    pthread_create(&consumer_tid,NULL,consumer,NULL);
    pthread_join(producer_tid,NULL);
    pthread_join(counter_tid,NULL);
    pthread_join(consumer_tid,NULL);
    return 0;
}
